package com.huan.es8.highlight;

import co.elastic.clients.elasticsearch._types.query_dsl.MatchQuery;
import co.elastic.clients.elasticsearch._types.query_dsl.Query;
import co.elastic.clients.elasticsearch.core.SearchRequest;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.elasticsearch.core.bulk.BulkOperation;
import co.elastic.clients.elasticsearch.core.search.Highlight;
import co.elastic.clients.elasticsearch.core.search.HighlightField;
import co.elastic.clients.elasticsearch.core.search.Hit;
import com.huan.es8.AbstractEs8Api;
import lombok.*;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 测试高亮
 *
 * <pre>
 * index mapping
 * PUT /index_book
 * {
 *   "settings": {
 *     "number_of_replicas": 1
 *   },
 *   "mappings": {
 *     "properties": {
 *       "id":{
 *         "type": "keyword",
 *         "copy_to": "all" # 将此字段的值 复制到 all 字段中
 *       },
 *       "name":{
 *         "type": "text",
 *         "analyzer": "ik_max_word",
 *         "index": true , # 为true表示此字段可以被搜索，即可以从此字段中搜索内容
 *         "copy_to": "all"
 *       },
 *       "all":{
 *         "type": "text"
 *       }
 *     }
 *   }
 * }
 *
 * </pre>
 *
 * @author huan.fu
 * @date 2022/10/31 - 23:28
 */
public class HighlightApiTest extends AbstractEs8Api {

    @DisplayName("高亮查询")
    @Test
    public void highlight() throws IOException, InterruptedException {
        // 创建索引
        createIndex();
        // 准备数据
        prepareData();

        // 需要睡眠1s,因为es最少1s后才可查询出刚插入的数据
        TimeUnit.SECONDS.sleep(1);

        // 需求：搜索字段all中含有 Java，并高亮

        // 构建query, 从 all 字段中查询 Java书籍
        Query query = new Query(new MatchQuery.Builder().field("all").query("Java书籍").build());

        // 高亮的字段
        Highlight highlight = new Highlight.Builder()
                // 高亮name字段
                .fields("name", new HighlightField.Builder()
                        .preTags("<em>")
                        .postTags("</em>")
                        // 此处我们高亮的字段是 name，但是我们查询的字段是 all，字段不匹配，就需要将requireFieldMatch设置成false
                        // 才会实现在name字段中实现高亮
                        .requireFieldMatch(false)
                        .build())
                .build();


        SearchRequest searchRequest = new SearchRequest.Builder()
                .query(query)
                .highlight(highlight)
                .build();

        System.err.println("request:" + searchRequest);

        SearchResponse<Book> searchResponse = client.search(searchRequest, Book.class);

        List<Hit<Book>> hits = searchResponse.hits().hits();
        for (Hit<Book> hit : hits) {
            Map<String, List<String>> hitHighlight = hit.highlight();
            Book sourceBook = hit.source();
            if (hitHighlight.containsKey("name")) {
                List<String> highlightNames = hitHighlight.get("name");
                sourceBook.setName(String.join(",", highlightNames));
            }
            System.err.println("sourceBook: " + sourceBook);
        }
        // 删除索引
        deleteIndex("index_book");
    }

    private void createIndex() throws IOException {
        String indexJson = "{\n" +
                "  \"settings\": {\n" +
                "    \"number_of_replicas\": 1\n" +
                "  },\n" +
                "  \"mappings\": {\n" +
                "    \"properties\": {\n" +
                "      \"id\":{\n" +
                "        \"type\": \"keyword\",\n" +
                "        \"copy_to\": \"all\" \n" +
                "      },\n" +
                "      \"name\":{\n" +
                "        \"type\": \"text\",\n" +
                "        \"analyzer\": \"ik_max_word\",\n" +
                "        \"index\": true , \n" +
                "        \"copy_to\": \"all\"\n" +
                "      },\n" +
                "      \"all\":{\n" +
                "        \"type\": \"text\"\n" +
                "      }\n" +
                "    }\n" +
                "  }\n" +
                "}";

        createIndex("index_book", indexJson);
    }

    private void prepareData() throws IOException {
        Book b1 = new Book("1001", "Java编程思想");
        Book b2 = new Book("1002", "Java从入门到放弃");
        Book b3 = new Book("1003 Java", "架构之道");

        List<BulkOperation> bulkOperations = new ArrayList<>(3);

        for (Book book : Arrays.asList(b1, b2, b3)) {
            BulkOperation bulkOperation = BulkOperation.of(operation ->
                    operation.create(create -> create.id(book.getId()).index("index_book").document(book)));
            bulkOperations.add(bulkOperation);
        }

        client.bulk(builder -> builder.operations(bulkOperations));
    }


    @AllArgsConstructor
    @NoArgsConstructor
    @Getter
    @Setter
    @ToString
    static class Book {
        private String id;
        private String name;
    }
}
